'''
Created on 26/04/2010

@author: kivson
'''
#from itertools import count
from geraCod.MepaTools import MTools

class AnaSintatico(object):
    '''
    Analisador Sintatico de Lalg
    '''


    def __init__(self, TSR):
        '''
        Constructor
        '''
        self._mepaTools = MTools()
        self.__tabelaSimbolos = TSR
        self.__tokens = []

    def get_tabela_simbolos(self):
        return self.__tabelaSimbolos

        
    def proximoToken(self):
        self.indexListaTokens += 1
        return self.__tokens[(self.indexListaTokens)]
    
    def devolveToken(self, token):
        self.indexListaTokens -= 1
        
    def analisa(self, tokens):
        self.indexListaTokens = -1
        self.__tokens = tokens
        self.programa(escopo='global')
        #print "concluido sem erros"
        
        
    def programa(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'program':
            token = self.proximoToken()
            if token[1] == '<identificador>':
                self.__tabelaSimbolos.addToken((token[0],kargs['escopo']),'programa')
                corpoCod= self.corpo(escopo='global')
                token = self.proximoToken()
                if token[0]=='.':
                    self.mepaCode = "INPP\n%sPARA" % corpoCod
                else:
                    self.erro()
            else:
                self.erro()
        else:
            self.erro()
    
    def corpo(self, **kargs):
        dcCod = self.dc(escopo=kargs['escopo'])
        token = self.proximoToken()
        if token[0] == 'begin':
            cmdCod = self.comandos(escopo=kargs['escopo'])
            token = self.proximoToken()
            if token[0] == 'end':
                return "%s%s" % (dcCod, cmdCod)
            else:
                self.erro()
        else:
            self.erro()
    #resolver problema com o mais_DC
    def dc(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'var':
            self.devolveToken(token)
            dcCod = self.dc_v(escopo=kargs['escopo'])
            mdcCod = self.mais_dc(escopo=kargs['escopo'])
            return  "%s%s" % (dcCod, mdcCod)
        elif token[0] == 'procedure':
            self.devolveToken(token)
            dcCod = self.dc_p(escopo=kargs['escopo'])
            mdcCod = self.mais_dc(escopo=kargs['escopo'])
            return "%s%s" % (dcCod, mdcCod)
        else:
            self.devolveToken(token)
            return ""
    
    def mais_dc(self, **kargs):
        token = self.proximoToken()
        if token[0] == ';':
            dcCod = self.dc(escopo=kargs['escopo'])
            return dcCod
        else:
            self.devolveToken(token)
            return ""
    
    def dc_v(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'var':
            """pass uma lista (variaveis) que recebera a lista das
                variaveis declaradas, para que possa ser atribuido o tipo"""
            variaveis = []
            self.variaveis(escopo=kargs['escopo'],listaVar=variaveis)
            token = self.proximoToken()
            if token[0] == ':':
                tipo = self.tipo_var(escopo=kargs['escopo'])
                """Atribui o tipo as variaveis retornadas por self.variaveis()"""
                for var in variaveis:
                    if self.existeIdentLocal(*var):
                        self.erro("Tente outro nome para a variavel: %s!!!"%var[0])
                    self.__tabelaSimbolos.addToken(var,'variavel')
                    self.__tabelaSimbolos.addAtributo(var, "tipo", tipo)
                    self.__tabelaSimbolos.addAtributo(var, "PilhaMepa", self._mepaTools.getPosPilha(kargs['escopo']))
                return "AMEN %d\n" % len(variaveis)
            else:
                self.erro()
        else:
            self.erro()
    #sem MEPA?!?
    def tipo_var(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'integer':
            return 'integer'
        elif token[0] == 'real':
            return 'real'
        else : 
            self.erro()
    #sem MEPA?!?
    def variaveis(self, **kargs):
        token = self.proximoToken()
        if token[1] == '<identificador>':
            kargs['listaVar'].append((token[0],kargs['escopo']))
            self.mais_var(escopo=kargs['escopo'],listaVar=kargs['listaVar'])
        else :
            self.erro()
    #sem MEPA?!?
    def mais_var(self, **kargs):
        token = self.proximoToken()
        if token[0] == ',':
            self.variaveis(escopo=kargs['escopo'],listaVar=kargs['listaVar'])
        else:
            self.devolveToken(token)
            return 
    


    def dc_p(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'procedure':
            token = self.proximoToken()
            if token[1] == '<identificador>':
                if self.existeIdentLocal(token[0], kargs['escopo']):
                    self.erro("Tente outro nome para o procedimento: %s" % token[0])
                self.__tabelaSimbolos.addToken((token[0],kargs['escopo']),'procedimento')
                self.__tabelaSimbolos.addAtributo((token[0],kargs['escopo']), "Label", self._mepaTools.getLabel())
                #pega lsita de parametros
                parametros = []
                self.parametros(escopo=token[0], param = parametros)
                #pega os tipos de parametros do procedimentos e adiciona a sua entrada na tabela
                self.adicionaListaDeTiposDeParametros(kargs, token, parametros)
                corpoCod = self.corpo_p(escopo=token[0])
                proc = self.existeIdent(token[0], kargs['escopo'])
                labelProtetor = self._mepaTools.getLabel()
                return "DSVS %s\n%s NADA\nIPVL %d\n%s%s NADA\n" % (labelProtetor, proc["Label"], len(parametros),corpoCod, labelProtetor)
            else:
                self.erro()
        else :
            self.erro()
    #sem MEPA?!?
    def parametros(self, **kargs):
        token = self.proximoToken()
        if token[0] == '(':
            self.lista_par(escopo=kargs['escopo'], param = kargs['param'])
            #print parametros
            token = self.proximoToken()
            if token[0] == ')':
                return
            else:
                self.erro()
        else:
            self.devolveToken(token)
            return
    #sem MEPA?!?  
    def lista_par(self, **kargs):
        variaveis = []
        self.variaveis(escopo=kargs['escopo'],listaVar=variaveis)
        token = self.proximoToken()
        if token[0] == ':':
            tipo = self.tipo_var()
            for var in variaveis:
                if self.existeIdentLocal(*var):
                    self.erro("Tente outro nome para o parametro: %s!!!"%var[0])
                self.__tabelaSimbolos.addToken(var,'parametro')
                self.__tabelaSimbolos.addAtributo(var, "tipo", tipo)
                self.__tabelaSimbolos.addAtributo(var, "PilhaMepa", self._mepaTools.getPosPilha(kargs['escopo']))

            kargs['param'] += variaveis
            self.mais_par(escopo=kargs['escopo'], param = kargs['param'])
        else:
            self.erro()
    #sem MEPA?!?
    def mais_par(self, **kargs):
        token = self.proximoToken()
        if token[0] == ';':
            self.lista_par(escopo=kargs['escopo'] , param = kargs['param'])
        else:
            self.devolveToken(token)
            return
    
    def corpo_p(self, **kargs):
        dcCod = self.dc_loc(escopo=kargs['escopo'])
        token = self.proximoToken()
        if token[0] == 'begin':
            cmdCod = self.comandos(escopo=kargs['escopo'])
            token = self.proximoToken()
            if token[0] == 'end':
                return "%s%sRTPR\n" % (dcCod,cmdCod)
            else:
                self.erro()
        else:
            self.erro()
    
    def dc_loc(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'var':
            self.devolveToken(token)
            dcCod = self.dc_v(escopo=kargs['escopo'])
            mdcCod = self.mais_dcloc()
            return "%s%s" % (dcCod, mdcCod)
        else:
            self.devolveToken(token)
            return ""
    
    def mais_dcloc(self, **kargs):
        token = self.proximoToken()
        if token[0] == ';':
            dclocCod  = self.dc_loc()
            return dclocCod
        else:
            self.devolveToken(token)
            return ""
    
    def lista_arg(self, **kargs):
        token = self.proximoToken()
        if token[0] == '(':
            self.argumentos(escopo=kargs['escopo'], args = kargs['args'])
            token = self.proximoToken()
            if token[0] == ')':
                return
            else:
                self.erro()
        else:
            self.devolveToken(token)
            return
    
    def argumentos(self, **kargs):
        token = self.proximoToken()
        if token[1] == '<identificador>':
            argumento = self.existeIdent(token[0], kargs['escopo'])
            if not argumento:
                self.erro("variavel nao declarada")
            if argumento:
                if argumento['classe'] != 'variavel' and argumento['classe'] != 'parametro':
                    self.erro("Esperado uma variavel ou parametro, encontrado um %s."% argumento['classe'])
            kargs['args'].append(argumento)
            self.mais_ident(escopo=kargs['escopo'], args = kargs['args'])
        else:
            self.erro()
    
    def mais_ident(self, **kargs):
        token = self.proximoToken()
        if token[0] == ';':
            self.argumentos(escopo=kargs['escopo'], args = kargs['args'])
        else:
            self.devolveToken(token)
            return
        
    def pfalsa(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'else':
            cmdCod = self.comandos(escopo=kargs['escopo'])
            return cmdCod
        else:
            self.devolveToken(token)
            return ""
        
    def comandos(self, **kargs):
        cmdCod = self.comando(escopo=kargs['escopo'])
        mcmdCod = self.mais_comandos(escopo=kargs['escopo'])
        return "%s%s" % (cmdCod,mcmdCod)
    
    def mais_comandos(self, **kargs):
        token = self.proximoToken()
        if token[0] == ';':
            cmdCod = self.comandos(escopo=kargs['escopo'])
            return cmdCod
        else:
            self.devolveToken(token)
            return ""
    
    def comando(self, **kargs):
        token = self.proximoToken()
        if token[0] == 'read':
            token = self.proximoToken()
            if token[0] == '(':
                variaveis = []
                self.variaveis(escopo=kargs['escopo'],listaVar=variaveis)
                cod = []
                for var in variaveis:
                    varToken = self.existeIdent(*var)
                    if not varToken :
                        self.erro("Tente outro nome para a variavel: %s!!!"%var[0])
                    if (varToken['escopo'] == "global"):
                        cod.append( "LEIT\nARMZ %d %d\n" % (varToken["PilhaMepa"], 0) )
                    else:
                        cod.append( "LEIT\nARMZ %d\n" % varToken["PilhaMepa"] )    
                    
                token = self.proximoToken()
                if token[0] == ')':
                    return ''.join(cod)
                else:
                    self.erro()
            else:
                self.erro()
        elif token[0] == 'write':
            token = self.proximoToken()
            if token[0] == '(':
                variaveis = []
                self.variaveis(escopo=kargs['escopo'],listaVar=variaveis)
                cod = []
                for var in variaveis:
                    varToken = self.existeIdent(*var)
                    if not self.existeIdent(*var):
                        self.erro("Tente outro nome para a variavel: %s!!!"%var[0])
                    if varToken['escopo'] == 'global':
                        cod.append( "CRVL %d %d\nIMPR\n" % (varToken["PilhaMepa"], 0) )
                    else:
                        cod.append( "CRVL %d\nIMPR\n" % varToken["PilhaMepa"] )
                    
                token = self.proximoToken()
                if token[0] == ')':
                    return  ''.join(cod)
                else:
                    self.erro()
            else:
                self.erro()
        elif token[0] == 'while':
            condCod = self.condicao(escopo=kargs['escopo'])
            token = self.proximoToken()
            if token[0] == 'do':
                conmCod = self.comandos(escopo=kargs['escopo'])
                token = self.proximoToken()
                if token[0] == '$':
                    label1 = self._mepaTools.getLabel()
                    label2 = self._mepaTools.getLabel()
                    return "%s NADA\n%sDSVF %s\n%sDSVS %s\n%s NADA\n" % (label1, condCod, label2, conmCod, label1, label2)
                else:
                    self.erro()
            else:
                self.erro()
        elif token[0] == 'if':
            condCod = self.condicao(escopo=kargs['escopo'])
            token = self.proximoToken()
            if token[0] == 'then':
                cmdCod = self.comandos(escopo=kargs['escopo'])               
                pfCod = self.pfalsa(escopo=kargs['escopo'])
                token = self.proximoToken()
                if token[0] == '$':
                    label1 = self._mepaTools.getLabel()
                    cod = "%sDSVF %s\n%s" % (condCod, label1, cmdCod)
                    if pfCod:
                        label2 = self._mepaTools.getLabel()
                        cod += "DSVS %s\n%s NADA\n%s%s NADA\n" % (label2, label1, pfCod, label2)
                    else:
                        cod += "%s NADA\n" % (label1)
                    return cod
                else:
                    self.erro()
            else:
                self.erro()
        elif token[1] == '<identificador>':
            ident = self.existeIdent(token[0], kargs['escopo'])
            if not ident:
                self.erro("Identificador nao declarado")
            #passar o token atual como ident para verificar o tipo de acordo com oresto da identacao.
            restCod = self.restoIdent(escopo=kargs['escopo'], ident=token)
            return restCod
        else:
            self.erro()
    
    def restoIdent(self, **kargs):
        identificador = self.existeIdent(kargs['ident'][0], kargs['escopo'])
        token = self.proximoToken()
        #se for uma atribuicaoo
        if token[0] == ':=':
            if identificador['classe'] != 'variavel' and identificador['classe'] != 'parametro':
                self.erro("Esperado uma identificador ou parametro, encontrado um %s."% identificador['classe'])
            listaTipos = []
            expCod = self.expressao(escopo=kargs['escopo'], listaT= listaTipos)
            tipo = listaTipos[0]
            #verifica se o tipo do identificador corresponde ao da expressao
            if identificador['tipo'] != tipo:
                self.erro("Conflito de Tipos na atribuicao")
            
            cod = "%sARMZ %s\n" % (expCod, identificador["PilhaMepa"])
            if (identificador['escopo'] == "global"):
                cod = "%sARMZ %s %d\n" % (expCod, identificador["PilhaMepa"], 0)
            return cod
            
        #se for a chamada de um procedimento
        else:
            if identificador['classe'] != 'procedimento':
                self.erro("Esperado uma procedimento, encontrado um %s."% identificador['classe'])
            self.devolveToken(token)
            #pega a lista de arguemtos da chamada
            listaArg = []
            self.lista_arg(escopo=kargs['escopo'], args=listaArg)
            #verifica se os tipos dos argumentos do procedimento sao iguais aos tipos dos arguemntos do mesmo
            args = []
            for num,argumento in enumerate(listaArg):
                if identificador['tiposParametos'][num] != argumento['tipo']:
                    self.erro("Parametros para %s incorretos" % identificador['nome'])
                if argumento['escopo'] == 'global':
                    args.append("CRVL %s %d\n" % (argumento["PilhaMepa"], 0))
                else:
                    args.append("CRVL %s\n" % (argumento["PilhaMepa"]))
            return "%sCHPR %s\n" % (''.join(args), identificador["Label"])
    
    def condicao(self, **kargs):
        listaTipos = []
        expCod1 = self.expressao(escopo=kargs['escopo'], listaT= listaTipos)
        tipo1 = listaTipos[0]
        relCod = self.relacao(escopo=kargs['escopo'])
        expCod2 = self.expressao(escopo=kargs['escopo'], listaT= listaTipos)
        tipo2 = listaTipos[0]
        if (tipo1 != tipo2):
            self.erro("Condicao com tipos diferentes!!!")
        return "%s%s%s" % (expCod1, expCod2, relCod)
    
    def relacao(self, **kargs):
        token = self.proximoToken()
        if token[0] == '=':
            return "CMIG\n"
        elif token[0] == '<>':
            return "CMGD\n"
        elif token[0] == '>=':
            return "CMAG\n"
        elif token[0] == '<=':
            return "CMEG\n"
        elif token[0] == '>':
            return "CMMA\n"
        elif token[0] == '<':
            return "CMME\n"
        else:
            self.erro()
            
            
    def expressao(self, **kargs):
        termCod = self.termo(escopo=kargs['escopo'], listaT=kargs['listaT'])
        oterCod = self.outros_termos(escopo=kargs['escopo'], listaT=kargs['listaT'])
        #verigfica se todos os tipos da expressaosao iguais
        for tipo in kargs['listaT']:
            if tipo != kargs['listaT'][0]:
                self.erro("conflito de Tipos")
        #print "%s%s%d\n\n" % (termCod, oterCod,self.__tokens[(self.indexListaTokens)][2])
        return "%s%s" % (termCod, oterCod)
        
    
    def op_un(self, **kargs):
        token = self.proximoToken()
        if token[0] == '+':
            return ""
        elif token[0] == '-':
            return "NEGA\n"
        else:
            self.devolveToken(token)
            return ""
    
    def outros_termos(self, **kargs):
        token = self.proximoToken()
        if token[0] == '+' or token[0] == '-':
            self.devolveToken(token)
            adCod = self.op_ad(escopo=kargs['escopo'])
            termCod = self.termo(escopo=kargs['escopo'], listaT=kargs['listaT'])
            oterCod = self.outros_termos(escopo=kargs['escopo'], listaT=kargs['listaT'])
            return "%s%s%s" % ( termCod, adCod, oterCod ) 
        else:
            self.devolveToken(token)
            return ""
    
    def op_ad(self, **kargs):
        token = self.proximoToken()
        if token[0] == '+':
            return "SOMA\n"
        elif token[0] == '-':
            return "SUBT\n"
        else:
            self.erro()
            
    def termo(self, **kargs):
        opunCod = self.op_un(escopo=kargs['escopo'])
        fatCod = self.fator(escopo=kargs['escopo'], listaT=kargs['listaT'])
        mfatCod = self.mais_fatores(escopo=kargs['escopo'], listaT=kargs['listaT'])
        return "%s%s%s" % (fatCod, opunCod, mfatCod)
        
    def mais_fatores(self, **kargs):
        token = self.proximoToken()
        if token[0] == '*' or token[0] == '/':
            self.devolveToken(token)
            mutCod = self.op_mul(escopo=kargs['escopo'])
            fatCod = self.fator(escopo=kargs['escopo'], listaT=kargs['listaT'])
            mfatCod = self.mais_fatores(escopo=kargs['escopo'], listaT=kargs['listaT'])
            return "%s%s%s" % (fatCod, mutCod, mfatCod)
        else:
            self.devolveToken(token)
            return ""
    
    def op_mul(self, **kargs):
        token = self.proximoToken()
        if token[0] == '*':
            return "MULT\n"
        elif token[0] == '/':
            return "DIVI\n"
        else:
            self.erro()
    
    def fator(self, **kargs):
        token = self.proximoToken()
        if token[1] == '<identificador>':
            fat = self.existeIdent(token[0], kargs['escopo'])
            if not fat:
                self.erro("variavel nao declarada")
            if fat:
                if fat['classe'] != 'variavel' and fat['classe'] != 'parametro':
                    self.erro("Esperado uma variavel ou parametro, encontrado um %s."% fat['classe'])
            kargs['listaT'].append(fat['tipo'])
            if (fat['escopo'] == 'global'):
                return "CRVL %d %d\n" % (fat["PilhaMepa"], 0)
            else:
                return "CRVL %d\n" % fat["PilhaMepa"]
        elif token[1] == '<inteiro>':
            kargs['listaT'].append('integer')
            return "CRCT %s\n" % token[0]
        elif token[1] == '<real>':
            kargs['listaT'].append('real')
            return "CRCT %s\n" % token[0]
        elif token[0] == '(':
            expCod = self.expressao(escopo=kargs['escopo'], listaT=kargs['listaT'])
            token = self.proximoToken()
            if token[0] == ')':
                return expCod
            else:
                self.erro()
        else:
            self.erro()
    
    def adicionaListaDeTiposDeParametros(self, kargs, token, parametros):
        tipos = []
        for parametro in parametros:
            var = self.__tabelaSimbolos.getToken(parametro)
            tipos.append(var['tipo'])
        
        self.__tabelaSimbolos.addAtributo((token[0], kargs['escopo']), "tiposParametos", tipos)
    
    def existeIdent(self,nome, escopo):
        if self.__tabelaSimbolos.getToken((nome,escopo)):
            return self.__tabelaSimbolos.getToken((nome,escopo))
        if self.__tabelaSimbolos.getToken((nome,'global')):
            return self.__tabelaSimbolos.getToken((nome,'global'))
        return None
    def existeIdentLocal(self,nome, escopo):
        if self.__tabelaSimbolos.getToken((nome,escopo)):
            return self.__tabelaSimbolos.getToken((nome,escopo))
        return None
    def erro(self, erro=None, **kargs):
        if erro:
            print "Erro: %s" % erro
        print 'Erro em: ', self.__tokens[(self.indexListaTokens)]
        raise SystemExit
    
    
    tabelaSimbolos = property(get_tabela_simbolos, None, None, None)
        


if __name__=='__main__':
    import Base
    import ALex.AnalisadorLexico
    
    tabelaF = file ('../Tabela.txt')
    tabelaStr = tabelaF.read()
    tabela = Base.TSR.TabelaRS();
    tabela.carregaTabelaString(tabelaStr)
    f = file('../lalg')
    s = f.read()
    LEX = ALex.AnalisadorLexico.AnaLex(tabela, s )
    LEX.analisa()
    SIN = AnaSintatico(LEX.tabelaSimbolos)
    #print LEX.getListTokens()
    SIN.analisa(LEX.getListTokens())
#    for c,i in SIN.tabelaSimbolos.tokens.iteritems():
#        if i == None: continue
#        if i['classe']!= 'operador' and i['classe']!= 'palavra_resservada':
#            print c, i
    print SIN.mepaCode